/*
 * Copyright © 2024-2025 Wenze Wei
 *
 * This file is part of StadionOS.
 *
 * This Source Code Form is subject to the terms of the GNU General Public License, v. 2.0.
 * You can obtain a copy of the GPL at
 * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 */

#include "compositor.h"

#define MOD_NONE 0
#define MOD_CTRL (1 << 0)
#define MOD_ALT (1 << 1)
#define MOD_SHIFT (1 << 2)

#define RESIZE_NONE 0
#define RESIZE_LEFT 1
#define RESIZE_RIGHT 2
#define RESIZE_TOP 3
#define RESIZE_BOTTOM 4

static uint32_t modifiers = 0;
static xkb_keycode_t keycode = 0;
static struct onatu_compositor *compositor = NULL;

static void handle_keyboard_key(struct wl_listener *listener, void *data) {
    struct wlr_keyboard_key_event *event = data;
    keycode = event->keycode;
    modifiers = wlr_keyboard_get_modifiers(wlr_keyboard_from_input_device(event->device));

    if (modifiers == WLR_MODIFIER_LOGO && keycode == XKB_KEY_Return) {
        compositor->current_layout = 
            (compositor->current_layout == LAYOUT_TILED) ? 
            LAYOUT_FLOAT : LAYOUT_TILED;
        tile_windows(compositor);
    }
}

void toggle_tiling(struct onatu_compositor *compositor) {
    compositor->current_layout = LAYOUT_TILED;
    tile_windows(compositor);
}

void toggle_stacking(struct onatu_compositor *compositor) {
    compositor->current_layout = LAYOUT_STACKED;
    stack_windows(compositor);
}

void close_window(struct onatu_compositor *compositor) {
    if (compositor->focused_window) {
        compositor->close_window(compositor->focused_window);
    }
}

void toggle_floating(struct onatu_compositor *compositor) {
    if (compositor->focused_window) {
        compositor->focused_window->is_floating = !compositor->focused_window->is_floating;
    }
}

static void handle_titlebar_button(struct wl_listener *listener, void *data) {
    struct onatu_compositor *compositor = wl_container_of(listener, compositor, titlebar_button_press);
    if (compositor->focused_window) {
        compositor->close_window(compositor->focused_window);
    }
}

void setup_window_decoration(struct onatu_window *window) {
    window->compositor->titlebar_bg = wlr_scene_rect_create(&window->compositor->scene->tree, 400, 30, (float[4]){0.2, 0.2, 0.2, 1.0});
    wlr_scene_node_set_position(&window->compositor->titlebar_bg->node, window->x, window->y - 30);

    window->compositor->close_btn = wlr_scene_button_create(&window->compositor->scene->tree, 30, 30, (float[4]){1.0, 0.0, 0.0, 1.0});
    wlr_scene_node_set_position(&window->compositor->close_btn->node, window->x + 370, window->y - 28);

    window->compositor->titlebar_button_press.notify = handle_titlebar_button;
    wl_signal_add(&window->compositor->close_btn->events.click, &window->compositor->titlebar_button_press);
}

void sapi_register_pointer_handler(void (*handler)(int, int, int)) {
    compositor->pointer_handler = handler;
}

void move_window_to_output(struct onatu_window *window, struct onatu_output *output) {
    window->x = output->display_config.logical_x + (output->effective_area.width - window->width)/2;
    window->y = output->display_config.logical_y + (output->effective_area.height - window->height)/2;
    wlr_scene_node_set_position(&window->scene_node->node, window->x, window->y);
}

void pointer_add(struct onatu_compositor *compositor, struct wlr_input_device *device) {
    struct wlr_pointer *pointer = wlr_pointer_from_input_device(device);

    struct wl_listener *listener = calloc(1, sizeof(struct wl_listener));
    listener->notify = pointer_motion_notify;
    wl_signal_add(&pointer->events.motion, listener);

    wl_list_insert(&compositor->pointers, &listener->link);

    if (compositor->focused_window) {
        setup_window_decoration(compositor->focused_window);
    }
    printf("New pointer detected: %s\n", device->name);
}

static void sapi_key_handler(struct wl_listener *listener, void *data) {
    struct wlr_keyboard_key_event *event = data;
    if (compositor->key_handler) {
        compositor->key_handler(event->keycode, wlr_keyboard_get_modifiers(compositor->keyboard));
    }
}

static void keyboard_key_notify(struct wl_listener *listener, void *data) {
    struct wlr_keyboard_key_event *event = data;
    struct onatu_compositor *compositor = wl_container_of(listener, compositor, new_input);
    struct key_binding *active_bindings = NULL;
    size_t binding_count = 0;
    load_keybindings(&active_bindings, &binding_count);

    for (size_t i = 0; i < binding_count; i++) {
        if (event->keycode == active_bindings[i].keycode && 
            (modifiers & (MOD_CTRL | MOD_ALT | MOD_SHIFT)) == active_bindings[i].modifiers) {
            active_bindings[i].handler(compositor);
            return;
        }
    }
}

static void pointer_button_notify(struct wl_listener *listener, void *data) {
    struct wlr_pointer_button_event *event = data;
    struct onatu_compositor *compositor = wl_container_of(listener, compositor, new_input);
    
    if (event->state == WLR_BUTTON_PRESSED) {
        struct onatu_window *clicked_window = get_window_at(compositor, event->x, event->y);
        if (clicked_window) {
            // 提升被点击窗口的层级
            clicked_window->z_index = ++compositor->max_z_index;
            sort_windows_by_zindex(compositor);
            wlr_scene_node_raise_to_top(clicked_window->scene_node);
            focus_window(compositor, clicked_window);
        }
    }
    if (compositor->grabbed_window) {
        int border_margin = 5;
        int window_right = compositor->grabbed_window->x + compositor->grabbed_window->width;
        int window_bottom = compositor->grabbed_window->y + compositor->grabbed_window->height;

        if (event->x >= window_right - border_margin) {
            compositor->grabbed_window->resize_edge = RESIZE_RIGHT;
        } else if (event->x <= compositor->grabbed_window->x + border_margin) {
            compositor->grabbed_window->resize_edge = RESIZE_LEFT;
        } else if (event->y >= window_bottom - border_margin) {
            compositor->grabbed_window->resize_edge = RESIZE_BOTTOM;
        } else if (event->y <= compositor->grabbed_window->y + border_margin) {
            compositor->grabbed_window->resize_edge = RESIZE_TOP;
        } else {
            compositor->grabbed_window->resize_edge = RESIZE_NONE;
        }

        compositor->grabbed_window->resizing = 
            (compositor->grabbed_window->resize_edge != RESIZE_NONE);
    }

    if (compositor->grabbed_window && 
        event->x >= compositor->grabbed_window->x &&
        event->x <= compositor->grabbed_window->x + compositor->grabbed_window->width &&
        event->y >= compositor->grabbed_window->y - 30 &&
        event->y <= compositor->grabbed_window->y) {
        compositor->grabbed_window->is_dragging = true;
        compositor->grabbed_window->resizing = false;
    }
    } else {
        if (compositor->grabbed_window && compositor->grabbed_window->resizing) {
            int dx = event->delta_x * 0.15;
            int dy = event->delta_y * 0.15;
            
            struct onatu_output *current_output = compositor->focused_output;
            int window_right = compositor->grabbed_window->x + compositor->grabbed_window->width;
            int window_bottom = compositor->grabbed_window->y + compositor->grabbed_window->height;
            
            if (window_right > current_output->display_config.logical_x + current_output->effective_area.width) {
                struct onatu_output *next = wl_container_of(current_output->linked_outputs.next, next, link);
                move_window_to_output(compositor->grabbed_window, next);
            } else if (compositor->grabbed_window->x < current_output->display_config.logical_x) {
                struct onatu_output *prev = wl_container_of(current_output->linked_outputs.prev, prev, link);
                move_window_to_output(compositor->grabbed_window, prev);
            }
            
            switch (compositor->grabbed_window->resize_edge) {
                case RESIZE_LEFT:
                    compositor->grabbed_window->x += dx;
                    compositor->grabbed_window->width -= dx;
                    break;
                case RESIZE_RIGHT:
                    compositor->grabbed_window->width += dx;
                    break;
                case RESIZE_TOP:
                    compositor->grabbed_window->y += dy;
                    compositor->grabbed_window->height -= dy;
                    break;
                case RESIZE_BOTTOM:
                    compositor->grabbed_window->height += dy;
                    break;
                default: break;
            }
            
            wlr_xdg_toplevel_set_size(compositor->grabbed_window->xdg_surface->toplevel,
                compositor->grabbed_window->width,
                compositor->grabbed_window->height);
             
            wlr_scene_node_set_position(&compositor->grabbed_window->titlebar_bg->node,
                compositor->grabbed_window->x, compositor->grabbed_window->y - 30);
        } else if (compositor->grabbed_window) {
            compositor->grabbed_window->is_dragging = false;
        }
    }
}

static void sapi_pointer_handler(struct wl_listener *listener, void *data) {
    struct wlr_pointer_motion_event *event = data;
    if (compositor->pointer_handler) {
        compositor->pointer_handler(event->delta_x, event->delta_y, 0);
    }
}

static void pointer_motion_notify(struct wl_listener *listener, void *data) {
    struct onatu_compositor *compositor = wl_container_of(listener, compositor, new_input);
    struct wlr_pointer_motion_event *event = data;
    const double ACCELERATION_FACTOR = 0.15;

    if (compositor->grabbed_window && compositor->grabbed_window->resizing) {
        int dx = event->delta_x * ACCELERATION_FACTOR;
        int dy = event->delta_y * ACCELERATION_FACTOR;
        
        switch (compositor->grabbed_window->resize_edge) {
            case RESIZE_LEFT:
                compositor->grabbed_window->x += dx;
                compositor->grabbed_window->width -= dx;
                break;
            case RESIZE_RIGHT:
                compositor->grabbed_window->width += dx;
                break;
            case RESIZE_TOP:
                compositor->grabbed_window->y += dy;
                compositor->grabbed_window->height -= dy;
                break;
            case RESIZE_BOTTOM:
                compositor->grabbed_window->height += dy;
                break;
            default: break;
        }
        
        wlr_xdg_toplevel_set_size(compositor->grabbed_window->xdg_surface->toplevel,
            compositor->grabbed_window->width,
            compositor->grabbed_window->height);
        
        wlr_scene_node_set_position(&compositor->grabbed_window->titlebar_bg->node,
            compositor->grabbed_window->x, compositor->grabbed_window->y - 30);
    } else if (compositor->grabbed_window) {
        compositor->grabbed_window->xdg_surface->surface->current.committed |= WLR_SURFACE_STATE_BUFFER;
        wlr_xdg_toplevel_set_position(compositor->grabbed_window->xdg_surface,
            event->delta_x + compositor->grabbed_window->x,
            event->delta_y + compositor->grabbed_window->y);
    }
    wlr_cursor_move(compositor->cursor, &event->pointer->base, 
        event->delta_x * ACCELERATION_FACTOR, 
        event->delta_y * ACCELERATION_FACTOR);
    wlr_seat_pointer_notify_motion(compositor->seat, event->time_msec, 
        compositor->cursor->x, compositor->cursor->y);

    struct onatu_window *window;
    wl_list_for_each_reverse(window, &compositor->windows, link) {
        if (wlr_surface_accepts_input(window->xdg_surface->surface)) {
            double sx, sy;
            if (wlr_surface_point_accepts_input(window->xdg_surface->surface,
                    compositor->cursor->x - window->x,
                    compositor->cursor->y - window->y,
                    &sx, &sy)) {
                focus_window(compositor, window);
                wlr_seat_pointer_notify_enter(compositor->seat, window->xdg_surface->surface, sx, sy);
                return;
            }
        }
    }
    wlr_seat_pointer_notify_clear_focus(compositor->seat);

    if (compositor->focused_window) {
        struct wlr_surface *surface = compositor->focused_window->xdg_surface->surface;
        wlr_seat_pointer_notify_enter(compositor->seat, surface, 
            compositor->cursor->x, compositor->cursor->y);
    }
}

static void adjust_window_zindex(struct onatu_compositor *compositor, int direction) {
    if (compositor->focused_window) {
        compositor->focused_window->z_index += direction;
        compositor->max_z_index = MAX(compositor->max_z_index, compositor->focused_window->z_index);
        sort_windows_by_zindex(compositor);
        wlr_scene_node_raise_to_top(compositor->focused_window->scene_node);
    }
}

static void raise_window_zindex(struct onatu_compositor *compositor) {
    adjust_window_zindex(compositor, 1);
}

static void lower_window_zindex(struct onatu_compositor *compositor) {
    adjust_window_zindex(compositor, -1);
}

static void load_keybindings(struct key_binding **bindings, size_t *count) {
    static struct key_binding defaults[] = {
        {XKB_KEY_F4, MOD_NONE, close_window},
        {XKB_KEY_T, MOD_CTRL, toggle_tiling},
        {XKB_KEY_S, MOD_CTRL, toggle_stacking},
        {XKB_KEY_Q, MOD_ALT, close_window},
        {XKB_KEY_Tab, MOD_ALT, cycle_focus_handler},
        {XKB_KEY_F, MOD_CTRL | MOD_SHIFT, toggle_floating},
        {XKB_KEY_Up, MOD_ALT | MOD_SHIFT, raise_window_zindex},
        {XKB_KEY_Down, MOD_ALT | MOD_SHIFT, lower_window_zindex},
        {XKB_KEY_Left, MOD_ALT | MOD_SHIFT, lower_window_zindex},
        {XKB_KEY_Right, MOD_ALT | MOD_SHIFT, raise_window_zindex
    };
    *bindings = defaults;
    *count = sizeof(defaults) / sizeof(defaults[0]);
}

static void handle_scale_gesture(double ratio) {
    // Handle scale gesture logic here
}

static void handle_touch_down(struct wl_listener *listener, void *data) {
    struct wlr_touch_down_event *event = data;
    
    struct touch_point *point = calloc(1, sizeof(*point));
    if (!point) {
        wlr_log(WLR_ERROR, "Failed to allocate touch point");
        return;
    }
    
    point->id = event->touch_id;
    point->start_x = event->x;
    point->start_y = event->y;
    point->current_x = event->x;
    point->current_y = event->y;
    clock_gettime(CLOCK_MONOTONIC, &point->start_time);
    point->last_update = point->start_time;
}

enum GESTURE_TYPE { GESTURE_NONE, GESTURE_PINCH, GESTURE_SWIPE, GESTURE_DOUBLE_TAP };

static enum GESTURE_TYPE detect_gesture(struct touch_point *points, int count) {
    static struct timespec last_tap_time = {0};
    
    if (count == 1 && points[0].state == TOUCH_STATE_END) {
        struct timespec now = points[0].timestamp;
        long elapsed = (now.tv_sec - last_tap_time.tv_sec) * 1000 +
                     (now.tv_nsec - last_tap_time.tv_nsec) / 1000000;
        if (elapsed < 300) {
            last_tap_time = (struct timespec){0};
            return GESTURE_DOUBLE_TAP;
        }
        last_tap_time = now;
    }
    
    if (count == 2 && points[0].state == TOUCH_STATE_UPDATE) {
        double dx = points[0].current_x - points[0].start_x;
        double dy = points[0].current_y - points[0].start_y;
        if (fabs(dx) > 50 || fabs(dy) > 50) {
            return GESTURE_SWIPE;
        }
    }
